AddCSLuaFile("cl_init.lua") 
AddCSLuaFile("shared.lua") 
include('shared.lua')

ENT.TrackIndexTriggerInformation = {}

local STATE_STOPPED = 0
local STATE_PAUSED = 2
local STATE_PLAYING = 1

function ENT:Initialize() 

	//For actor binding to track
	self.NumActors = 0
	self.ActorTable = {}
	self.OffsetTable = {}
	self:SetNWInt("VarNumActors", 0)
	self.ActorPlaybackState = -1

	self.BaseClass.Initialize(self, nil)	
end

//****************************************************
//* Called on a per frame basis, this iterates all actors
//* of the current node to see if any should be passed onto
//* a subsequent node
function ENT:ThinkActors()

	if not self.ActorTable or self.NumActors == 0 then
		return
	end

	local playerIndex = self.BaseClass.GetPlayerIndex(self)
	local trackIndex = self.BaseClass.GetTrackIndex(self)
	local nodeIndex = self.BaseClass.GetNodeIndex(self)
	local nodePosition = self:GetPos()
	
	local nodeCount = #CatmullRomCams.Tracks[playerIndex][trackIndex]
	
	local rootNode = CatmullRomCams.Tracks[playerIndex][trackIndex][1]

	if rootNode.ActorPlaybackState != STATE_PLAYING then
		return
	end

	for k, actor in pairs(self.ActorTable) do
		if self.BaseClass.IsActor(self, actor) then	
			local actorPosition = actor:GetPos()

			local nodePositionOffset = self.OffsetTable[actor:EntIndex()] + nodePosition
			local actorDestinationDistance = nodePositionOffset:Distance(actorPosition)
			
			if (actorDestinationDistance < 35) then
				
				if nodeIndex + 1 > nodeCount then
					rootNode.ActorPlaybackState = STATE_STOPPED
					self:ActorPlaybackComplete(actor, rootNode)
				else
					local nextNode = CatmullRomCams.Tracks[playerIndex][trackIndex][nodeIndex + 1]
					self:ActorPlaybackTransition(actor, nextNode)
				end

			else
				//Continue to set this incase the user repositions the node
				actor:SetLastPosition( nodePosition + self.OffsetTable[actor:EntIndex()])
				actor:SetSchedule( SCHED_FORCED_GO )
			end
		end
	end
end

//****************************************************
//*	Called externally to animate actors that are assigned
//* to this track. This function can be invoked from any 
//* node on the track, and will cause assigned actors to
//* animate from the root node
function ENT:PlayActors(animID)

	local playerIndex = self.BaseClass.GetPlayerIndex(self)
	local trackIndex = self.BaseClass.GetTrackIndex(self)

	//TODO:
	//look for actors across track and bring them back to starting node??

	local pathwayStartNode = CatmullRomCams.Tracks[playerIndex][trackIndex][1]
	if self == pathwayStartNode and self.ActorTable then

		local nextNode = CatmullRomCams.Tracks[playerIndex][trackIndex][2]
		if nextNode then
			
			for k, actor in pairs(self.ActorTable) do
				self:ActorPlaybackTransition(actor, nextNode)
			end
		else
			print("Track only has one node")
		end

		self.ActorPlaybackState = STATE_PLAYING

	else
		pathwayStartNode:PlayActors(animID)
	end
end

//****************************************************
//*	Only to be used during playback of actors by the
//* track itself. Returns true only if the actor animation
//* data was successfully transferred to destination node
function ENT:TransitionPlaybackData(actor, destinationNode)

	if not self.BaseClass.IsActor(self, actor) and self.BaseClass.IsPathNode(self, destinationNode) then
		return false
	end
	destinationNode.ActorTable[actor:EntIndex()] = actor
	destinationNode.OffsetTable[actor:EntIndex()] = self.OffsetTable[actor:EntIndex()]
	destinationNode.NumActors = destinationNode.NumActors + 1		
	destinationNode:SetNWInt("VarActorEntID", actor:EntIndex())
	destinationNode:SetNWInt("VarNumActors", destinationNode.NumActors)
						
	self.ActorTable = self.ActorTable or {}
	self.ActorTable[actor:EntIndex()] = actor
	self.OffsetTable[actor:EntIndex()] = nil
	self.ActorTable[actor:EntIndex()] = nil
	self.NumActors = self.NumActors - 1		
	self:SetNWInt("VarActorEntID", actor:EntIndex())
	self:SetNWInt("VarNumActors", self.NumActors)
	return true
end

//****************************************************
//*	Only to be used during playback of actors by the
//* track itself
function ENT:ActorPlaybackComplete(actor, startNode)

	if self:TransitionPlaybackData(actor, startNode) == true then
		actor:SetPos( startNode:GetPos() + startNode.OffsetTable[actor:EntIndex()])
		actor:SetLastPosition(startNode:GetPos() + startNode.OffsetTable[actor:EntIndex()])
		actor:SetSchedule( SCHED_NONE)
	end
end


//****************************************************
//*	Only to be used during playback of actors by the
//* track itself
function ENT:ActorPlaybackTransition(actor, destinationNode)

	if self:TransitionPlaybackData(actor, destinationNode) == true then
		actor:SetLastPosition( destinationNode:GetPos() + destinationNode.OffsetTable[actor:EntIndex()])
		actor:SetSchedule( SCHED_FORCED_GO )
	end
end

//****************************************************
//*	Enables an NPC/actor to be assigned to an actor
//* pathway. This will by default send the actor to the
//* first node. This should not typically be used for 
//* acutal animation of the actor when walking the path
function ENT:AssignActorToTrack(actor)

	if not self.BaseClass.IsActor(self, actor) then
		return false
	end

	local playerIndex = self.BaseClass.GetPlayerIndex(self)
	local trackIndex = self.BaseClass.GetTrackIndex(self)

	local pathwayStartNode = CatmullRomCams.Tracks[playerIndex][trackIndex][1]

	if self == pathwayStartNode then

		self.NumActors = self.NumActors + 1
		self.ActorTable = self.ActorTable or {}
		//It is important that we key the table by
		//the entities ID because it prevents mixups
		//when assigning an actor to the next node
		//from a group of actors on the current node
		self.ActorTable[actor:EntIndex()] = actor
		self.OffsetTable[actor:EntIndex()] = actor:GetPos() - self:GetPos()
		
		//This effectivly triggers the client so that
		//it looks to find the newly attached actor.
		//The order of these is important too, make sure
		//we get the entity ID out there before triggering
		//the client to look
		self:SetNWInt("VarActorEntID", actor:EntIndex())
		self:SetNWInt("VarNumActors", self.NumActors)

		print("\n*****************************")	
		print("NPC assigned to pathway node")	
		print("Actor ID: " .. actor:EntIndex())
		print("*****************************\n")	
		
		return true
	else
		return pathwayStartNode:AssignActorToTrack(actor)
	end
end

function ENT:UnassignNPC(npc)

end

function ENT:ClearHighlighting()

	local numCameras = #CatmullRomCams.Tracks[self.IndexData.PlayerIndex][self.IndexData.TrackIndex]
	for i = 1, numCameras do

		local nodeCamera = CatmullRomCams.Tracks[self.IndexData.PlayerIndex][self.IndexData.TrackIndex][i]:GetNWEntity("ChildCamera")
		if(nodeCamera != nil) then
			nodeCamera:SetNWBool("IsHighlighted", false)
		end
	end
end

function ENT:Think()
	if self.IsMapController then return end

	self:ThinkActors()
	self.Entity:NextThink(CurTime() + 1)
end

function ENT:PhysicsUpdate(physobj)
	if not self.Entity:IsPlayerHolding() then
		if self.IsMapController then
			physobj:EnableMotion(false)
		else
			physobj:Sleep()
		end
	end
end

function ENT:LoadForMap(filename)
	filename = filename or ""
print("function ENT:LoadForMap(filename)")	
	self:SetCollisionGroup(COLLISION_GROUP_NONE)
	self:SetMoveType(MOVETYPE_NONE)
	self:SetSolid(SOLID_NONE)
	
	self.IsMapController = true
	self:SetNWBool("IsMapController", true)
	
	if not CatmullRomCams then
		include("autorun/sh_CatmullRomCams")
	end
	
	self:InitController()
	
	local data = file.Read("CatmullRomCameraTracks/" .. filename .. ".txt")
	
	if not data then return Error("Could not load filename track '" .. filename .. "' from disk! " .. tostring(self) .. " map controller will not work!!!\n") end
	
	data = util.TrackIndexValuesToTable(data)
	
	for k, v in ipairs(data) do
	end
	
	local lastent = self
	local count = 1
	
	self.CatmullRomController.PointsList = {}
	self.CatmullRomController.AnglesList = {}
	
	self.CatmullRomController.DurationList = {}
	
	self.CatmullRomController.Spline = {}
	
	self.CatmullRomController:AddPointAngle(count, lastent:GetPos(), lastent:GetAngles())
	
	while true do
		count = count + 1
		
		local ent = lastent:GetNWEntity("ChildCamera")
		
		if ent and ent:IsValid() then
			lastent = ent
			
			local dur = lastent:GetNWFloat("Duration")
			
			self.CatmullRomController:AddPointAngle(count, lastent:GetPos(), lastent:GetAngles(), (dur > 0) and dur or 2)
			
			self.CatmullRomController.EntityList[count] = lastent
		else
			break
		end
	end
end

function ENT:KeyValue(k, v)
	if k == "TrackFilename" then
		return self:LoadForMap(v)
	end
end

function ENT:PreEntityCopy() -- build the DupeInfo table and save it as an entity mod
	return duplicator.StoreEntityModifier(self.Entity, "CatmullRomCamsDupData", self:RequestSaveData())
end

function ENT:PostEntityPaste(Player, Ent, CreatedEntities)
	local playerIndex = Player:UniqueID()
	
	if Ent.EntityMods and Ent.EntityMods.CatmullRomCamsDupData then
		--[[
		if not CatmullRomCams.Tracks[playerIndex][Ent.EntityMods.CatmullRomCamsDupData.IndexData.TrackIndex].IsLockedForLoad then  -- hackz
			for k, v in pairs(CatmullRomCams.Tracks[playerIndex][Ent.EntityMods.CatmullRomCamsDupData.IndexData.TrackIndex]) do
				if v and v:IsValid() then
					v:Remove()
				end
			end
			
			CatmullRomCams.Tracks[playerIndex][Ent.EntityMods.CatmullRomCamsDupData.IndexData.TrackIndex] = {}
			CatmullRomCams.Tracks[playerIndex][Ent.EntityMods.CatmullRomCamsDupData.IndexData.TrackIndex].IsLockedForLoad = true
		end
		--]]
		return CatmullRomCams.SV.AdvDupPaste(self, Player, playerIndex, Ent, CreatedEntities)
	else
		self:Remove()
	end
end

function ENT:BuildDupeInfo()
end

function ENT:ApplyDupeInfo(ply, ent, data, CreatedEntities)
end

function ENT.AfterPasteMods(ply, Ent, DupeInfo) -- Happens before PostEntityPaste for some reason <_<
end
duplicator.RegisterEntityModifier("CatmullRomCamsDupData", ENT.AfterPasteMods)
